package main

import (
	"net"
	"sync"
	"io"
	"log"
	"errors"
)

type Session struct {
	id        uint64
	db        net.Conn
	client    net.Conn
	closeOnce sync.Once
}

func (s *Session) Close() {
	s.closeOnce.Do(s.doClose)
}
func (s *Session) doClose() {
	s.db.Close()
	s.client.Close()
}

func (s *Session) readPacket(reader io.Reader) (*RawPacket, error) {
	header := make([]byte, 4)
	_, err := reader.Read(header)
	if err != nil {
		return nil, err
	}
	contentLen := uint32(header[0]) + uint32(header[1])<<8 + uint32(header[2])<<16
	data := make([]byte, contentLen+4)
	copy(data, header)
	_, err = reader.Read(data[4:])
	if err != nil {
		return nil, err
	}
	rp := &RawPacket{Len: contentLen, Seq: header[3], Raw: data}
	return rp, nil
}
func (s *Session) copyPacket(reader io.Reader, writer io.Writer) error {
	p, err := s.readPacket(reader)
	if err != nil {
		return err
	}
	_, err = writer.Write(p.Raw)
	return err
}
func (s *Session) Handshake() error {
	err := s.copyPacket(s.db, s.client)
	if err != nil {
		return err
	}
	err = s.copyPacket(s.client, s.db)
	if err != nil {
		return err
	}
	p, err := s.readPacket(s.db)
	if err != nil {
		return err
	}
	ph := RespHeader{Raw: p}
	if ph.OK() {
		log.Println("handshake succcess")
	} else {
		log.Println("handshake error", ph.Err())
		err = errors.New("handshake error")
	}
	_, err = s.client.Write(p.Raw)
	return err
}

func (s *Session) ReadRequest() (*Request, error) {
	p, err := s.readPacket(s.client)
	if err != nil {
		return nil, err
	}
	return &Request{Remote: s.client.RemoteAddr(), Raw: p}, nil
}

func (s *Session) ReadResponse() (*Response, error) {
	p, err := s.readPacket(s.db)
	if err != nil {
		return nil, err
	}
	resp := new(Response)
	resp.Header = &RespHeader{Raw: p}
	status := resp.Header.Raw.Data()[0]
	if status == 0x00 || status == 0xff {
		return resp, nil
	}
	log.Println("field list size", readInteger(resp.Header.Raw.Data()))
	pl := &PacketList{}
	var c *RawPacket
	for {
		c, err = s.readPacket(s.db)
		if err != nil {
			break
		}
		pl.Append(c)
		if pl.Full() {
			break
		}
	}
	if err != nil {
		return nil, err
	}
	resp.Fields = pl
	resp.Rows = &PacketList{}
	for {
		c, err = s.readPacket(s.db)
		if err != nil {
			break
		}
		resp.Rows.Append(c)
		if resp.Rows.Full() {
			break
		}
	}
	if err != nil {
		resp = nil
	}
	return resp, err
}
func (s *Session) WriteRequest(req *Request) error {
	_, err := s.db.Write(req.Raw.Raw)
	return err
}
func (s *Session) WriteResponse(resp *Response) error {
	_, err := s.client.Write(resp.Header.Raw.Raw)
	if err != nil {
		return err
	}
	if resp.Fields == nil {
		return nil
	}
	iterator := resp.Fields.Iterator()
	for iterator.HasNext() {
		p := iterator.Next()
		_, err = s.client.Write(p.Raw)
		if err != nil {
			break
		}
	}
	if err != nil {
		return err
	}
	if _, err := s.client.Write(resp.Fields.EOF.Raw); err != nil {
		return err
	}
	iterator = resp.Rows.Iterator()
	for iterator.HasNext() {
		p := iterator.Next()
		_, err = s.client.Write(p.Raw)
		if err != nil {
			break
		}
	}
	if err != nil {
		return err
	}
	_, err = s.client.Write(resp.Rows.EOF.Raw)
	return err
}
